﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HQHS.Library.Common.Colloection
{
    [Serializable]
    public class SynchronizedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICloneable
    {
        // Fields
        private IEqualityComparer<TKey> _comparer;
        private Dictionary<TKey, TValue> _innerDictionary;
        private ReaderWriterLockSlim _lock;
        private int _lockTimeoutMilliseconds;

        // Methods
        public SynchronizedDictionary()
        {
            this._innerDictionary = new Dictionary<TKey, TValue>();
            this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
            this._lockTimeoutMilliseconds = 0x493e0;
            this._innerDictionary = new Dictionary<TKey, TValue>();
        }

        public SynchronizedDictionary(IDictionary<TKey, TValue> dictionary)
        {
            this._innerDictionary = new Dictionary<TKey, TValue>();
            this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
            this._lockTimeoutMilliseconds = 0x493e0;
            this._innerDictionary = new Dictionary<TKey, TValue>(dictionary);
        }

        public SynchronizedDictionary(IEqualityComparer<TKey> comparer)
        {
            this._innerDictionary = new Dictionary<TKey, TValue>();
            this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
            this._lockTimeoutMilliseconds = 0x493e0;
            this._innerDictionary = new Dictionary<TKey, TValue>(comparer);
            this._comparer = comparer;
        }

        public SynchronizedDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        {
            this._innerDictionary = new Dictionary<TKey, TValue>();
            this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
            this._lockTimeoutMilliseconds = 0x493e0;
            this._innerDictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
            this._comparer = comparer;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
            try
            {
                this.InnerIDictionary.Add(item);
            }
            finally
            {
                this._lock.ExitWriteLock();
            }
        }

        public void Add(TKey key, TValue value)
        {
            if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
            try
            {
                this._innerDictionary.Add(key, value);
            }
            finally
            {
                this._lock.ExitWriteLock();
            }
        }

        public void Clear()
        {
            if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
            try
            {
                this._innerDictionary.Clear();
            }
            finally
            {
                this._lock.ExitWriteLock();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public object Clone()
        {
            SynchronizedDictionary<TKey, TValue> dictionary;
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                if (this._comparer == null)
                {
                    return new SynchronizedDictionary<TKey, TValue>(this._innerDictionary);
                }
                dictionary = new SynchronizedDictionary<TKey, TValue>(this._innerDictionary, this._comparer);
            }
            finally
            {
                this.ExitReadLock();
            }
            return dictionary;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            bool flag;
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                flag = this.InnerIDictionary.Contains(item);
            }
            finally
            {
                this._lock.ExitReadLock();
            }
            return flag;
        }

        public bool ContainsKey(TKey key)
        {
            bool flag;
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                flag = this._innerDictionary.ContainsKey(key);
            }
            finally
            {
                this._lock.ExitReadLock();
            }
            return flag;
        }

        public bool ContainsValue(TValue value)
        {
            bool flag;
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                flag = this._innerDictionary.ContainsValue(value);
            }
            finally
            {
                this._lock.ExitReadLock();
            }
            return flag;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                this.InnerIDictionary.CopyTo(array, arrayIndex);
            }
            finally
            {
                this._lock.ExitReadLock();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public void EnterReadLock()
        {
            this._lock.EnterReadLock();
        }

        public void EnterWriteLock()
        {
            if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
        }

        public void EnterWriteLock(int millisecondsTimeout)
        {
            if (!this._lock.TryEnterWriteLock(millisecondsTimeout))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public void ExitReadLock()
        {
            this._lock.ExitReadLock();
        }

        public void ExitWriteLock()
        {
            this._lock.ExitWriteLock();
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public System.Collections.Generic.IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return (IEnumerator<KeyValuePair<TKey, TValue>>)this._innerDictionary.GetEnumerator();
        }

        private System.Collections.IEnumerator GetEnumerator_IEnumerable()
        {
            return (IEnumerator)this._innerDictionary.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public TKey[] GetKeyArray()
        {
            TKey[] localArray2;
            if (!this.TryEnterReadLock())
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
            try
            {
                localArray2 = (TKey[])Array.CreateInstance(typeof(TKey), this.Count);
                this._innerDictionary.Keys.CopyTo(localArray2, 0);
            }
            finally
            {
                this.ExitReadLock();
            }
            return localArray2;
        }

        public TValue[] GetValueArray()
        {
            TValue[] localArray2;
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                localArray2 = (TValue[])Array.CreateInstance(typeof(TValue), this.Count);
                this._innerDictionary.Values.CopyTo(localArray2, 0);
            }
            finally
            {
                this.ExitReadLock();
            }
            return localArray2;
        }
        
        [Obsolete("Please use ExitReadLock() instead.", true), EditorBrowsable(EditorBrowsableState.Never)]
        public void ReleaseReaderLock()
        {
            throw new NotSupportedException();
        }

        [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Please use ExitWriteLock() instead.", true)]
        public void ReleaseWriterLock()
        {
            throw new NotSupportedException();
        }

        public bool Remove(TKey key)
        {
            bool flag;
            if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
            try
            {
                flag = this._innerDictionary.Remove(key);
            }
            finally
            {
                this._lock.ExitWriteLock();
            }
            return flag;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            bool flag;
            if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the write lock.");
            }
            try
            {
                flag = this.InnerIDictionary.Remove(item);
            }
            finally
            {
                this._lock.ExitWriteLock();
            }
            return flag;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool TryEnterReadLock()
        {
            return this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds);
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool TryEnterReadLock(int millisecondsTimeout)
        {
            return this._lock.TryEnterReadLock(millisecondsTimeout);
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            bool flag;
            if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
            {
                throw new ApplicationException("Timeout trying to enter the read lock.");
            }
            try
            {
                flag = this._innerDictionary.TryGetValue(key, out value);
            }
            finally
            {
                this._lock.ExitReadLock();
            }
            return flag;
        }

        // Properties
        public int Count
        {
            get
            {
                int count;
                if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
                {
                    throw new ApplicationException("Timeout trying to enter the read lock.");
                }
                try
                {
                    count = this._innerDictionary.Count;
                }
                finally
                {
                    this._lock.ExitReadLock();
                }
                return count;
            }
        }

        private IDictionary<TKey, TValue> InnerIDictionary
        {
            get
            {
                return this._innerDictionary;
            }
        }

        [Obsolete("Please use IsReadLockHeld instead.", true), EditorBrowsable(EditorBrowsableState.Never)]
        public bool IsReaderLockHeld
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool IsReadLockHeld
        {
            get
            {
                return this._lock.IsReadLockHeld;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                bool isReadOnly;
                if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
                {
                    throw new ApplicationException("Timeout trying to enter the read lock.");
                }
                try
                {
                    isReadOnly = this.InnerIDictionary.IsReadOnly;
                }
                finally
                {
                    this._lock.ExitReadLock();
                }
                return isReadOnly;
            }
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public bool IsWriteLockHeld
        {
            get
            {
                return this._lock.IsWriteLockHeld;
            }
        }

        [Obsolete("Please use IsWriteLockHeld instead.", true), EditorBrowsable(EditorBrowsableState.Never)]
        public bool IsWriterLockHeld
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                TValue local;
                if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
                {
                    throw new ApplicationException("Timeout trying to enter the read lock.");
                }
                try
                {
                    local = this._innerDictionary[key];
                }
                finally
                {
                    this._lock.ExitReadLock();
                }
                return local;
            }
            set
            {
                if (!this._lock.TryEnterWriteLock(this._lockTimeoutMilliseconds))
                {
                    throw new ApplicationException("Timeout trying to enter the write lock.");
                }
                try
                {
                    this._innerDictionary[key] = value;
                }
                finally
                {
                    this._lock.ExitWriteLock();
                }
            }
        }

        public ICollection<TKey> Keys
        {
            get
            {
                ICollection<TKey> keys;
                if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
                {
                    throw new ApplicationException("Timeout trying to enter the read lock.");
                }
                try
                {
                    keys = this._innerDictionary.Keys;
                }
                finally
                {
                    this._lock.ExitReadLock();
                }
                return keys;
            }
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public int LockTimeoutMilliseconds
        {
            get
            {
                return this._lockTimeoutMilliseconds;
            }
            set
            {
                this._lockTimeoutMilliseconds = value;
            }
        }

        public ICollection<TValue> Values
        {
            get
            {
                ICollection<TValue> values;
                if (!this._lock.TryEnterReadLock(this._lockTimeoutMilliseconds))
                {
                    throw new ApplicationException("Timeout trying to enter the read lock.");
                }
                try
                {
                    values = this._innerDictionary.Values;
                }
                finally
                {
                    this._lock.ExitReadLock();
                }
                return values;
            }
        }
    }
}
